/**
 * File: StatisticFileParser.java
 * Created by: mhaimel
 * Created on: 30 Jun 2009
 * CVS:  $Id: StatisticFileParser.java,v 1.1 2009/07/07 10:45:43 mhaimel Exp $
 */
package uk.ac.ebi.velvet.processor.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import javolution.util.FastList;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.ac.ebi.curtain.utils.io.impl.FileIO;
import uk.ac.ebi.velvet.VelvetUncheckedException;

/**
 * Stateful object  - need to be reset for new use
 * 
 * @author mhaimel
 *
 */
public class StatisticFileParser {
	// ID      lgth    out     in      long_cov        short1_cov      short1_Ocov     short2_cov      short2_Ocov

	private static final String HEADER_ID = "ID";
	private static final String HEADER_lgth = "lgth";
	private static final String HEADER_long_cov_start = "long";
	private static final String HEADER_short_cov_start = "short";
	private static final String HEADER_cov_end = "_cov";

	private Log log = LogFactory.getLog(this.getClass());
	
	private Map<String, List<Integer>> statsMap = new HashMap<String, List<Integer>>();
	private static final String _INF_VAL = "Inf";
	
	public static void main(String[] args) {
		StatisticFileParser parser = new StatisticFileParser();
		parser.parse(new File("/homes/mhaimel/resources/velvetProd/output/salmonella_lt2/simulation/no-error/full_31/stats.txt_0"));
		System.out.println(parser.getCoverageKeys());
		for(String key : parser.getCoverageKeys()){
			System.out.println(key + ": " + parser.getCoverageListFor(key).size());
		}
		System.out.println("Length: " + parser.getLengthList().size());
	}
	
	public StatisticFileParser() {
		
	}
	
	public List<Integer> getLengthList(){
		return getCoverageListFor(HEADER_lgth);
	}
	
	public List<Integer> getCoverageListFor(String key){
		List<Integer> list = statsMap.get(key);
		if(null == list){
			list = new FastList<Integer>();
			statsMap.put(key, list);
		}
		return list;
	}
	
	public Collection<String> getCoverageKeys(){
		Set<String> keySet = new HashSet<String>();
		for(String key : statsMap.keySet()){
			if(key.endsWith(HEADER_cov_end)){
				keySet.add(key);
			}
		}
		return keySet;
	}
	
	
	
	public void parse(File file){
		BufferedReader in = null;
		try {
			in = new FileIO(file).getBufferedReader();
			parse(in);
		} catch (IOException e) {
			throw new VelvetUncheckedException(
					"Problems reading file " + file,e);
		}finally{
			IOUtils.closeQuietly(in);
		}
	}
	
	public void parse(BufferedReader in, Map<Integer, String> idx) throws IOException {
		Integer[] posArr = new Integer[idx.size()];
		String[] keyArr = new String[idx.size()];
		int i = 0;
		for(Entry<Integer, String> entry : idx.entrySet()){
			posArr[i] = entry.getKey();
			keyArr[i] = entry.getValue();
			++i;
		}
		boolean usedEntry = false;
		String line = StringUtils.EMPTY;
		while((line = in.readLine()) != null){
			if(StringUtils.isNotBlank(line)){
				incrLineCnt();
				usedEntry = false;
				String[] splitArr = split(line);
				for(i = 0; i < posArr.length; ++i){
					Integer pos = posArr[i];
					String key = keyArr[i];
					Integer val = getInteger(splitArr[pos]);
					if(null == val){
						val = 0;
					}
					store(key,val);
				}
				if(usedEntry){
					incrEntryCnt();
				}
			}
		}
	}
	
	private Integer entryCnt = 0;
	private void incrEntryCnt() {
		++entryCnt;	
	}

	private Integer lineCnt = 0;
	private void incrLineCnt() {
		++lineCnt;	
	}
	
	public Integer getLineCnt() {
		return lineCnt;
	}
	
	public Integer getEntryCnt() {
		return entryCnt;
	}

	public void store(String key, Integer value){	
		List<Integer> list = getCoverageListFor(key);
		list.add(value);
	}
	
	/**
	 * First line has to be the id header - parsed using {@link #buildIdx(String)} <br>
	 * then forwarded to {@link #parse(BufferedReader, Map)}
	 * @param in
	 * @throws IOException 
	 */
	public void parse(BufferedReader in) throws IOException {
		String line = in.readLine();
		if(!isHeader(line)){
			throw new VelvetUncheckedException("Header line expected in line: " + line);
		}
		Map<Integer, String> idx = buildIdx(line);
		parse(in,idx);		
	}
	
	/**
	 * Selected fields (length, short/long_cov) will be used
	 * @param idx
	 * @return {@link Map} of Position (Integer) to Key (String)
	 */
	private Map<Integer, String> buildIdx(String idx){
		Map<Integer, String> idxMap = new HashMap<Integer, String>();
		String[] arr = split(idx);
		for(int i = 0; i < arr.length; ++i){
			String key = arr[i];
			if(isValidField(key)){
				idxMap.put(i, key);
			}
		}		
		return idxMap;
	}
	
	private boolean isValidField(String key) {
		if(key.equalsIgnoreCase(HEADER_ID)){
			return false;
		}else if(key.equalsIgnoreCase(HEADER_lgth)){
			return true;
		}else if(key.endsWith(HEADER_cov_end)){
			if(key.startsWith(HEADER_long_cov_start)){
				return false;
			}else if(key.startsWith(HEADER_short_cov_start)){
				return true;
			}
		}
		return false;
	}
	
	private Integer getInteger(String intStr){
		if(StringUtils.equals(intStr, _INF_VAL)){
			return null;
		}
		Double val = Double.valueOf(intStr);
		if(val.isInfinite() || val.isNaN()){
			return null;
		} else {
			// Don't round, otherwise you could loose data for 
			// supporting the right boundary instead of the left one
			return Long.valueOf(Math.round(val)).intValue();
		}
	}

	private static Pattern tabPat = Pattern.compile("\t");
	private String[] split(String line) {
		return tabPat.split(line);
	}
	
	private boolean isHeader(String line) {
		return line.startsWith(HEADER_ID);
	}

	public void reset(){
		
	}		
}
